package git.soulbgm.core;

import git.soulbgm.bean.BeanDefinition;
import git.soulbgm.bean.ConstructorArg;
import git.soulbgm.bean.PropertyArg;
import git.soulbgm.util.BeanUtil;
import git.soulbgm.util.ClassUtil;
import git.soulbgm.util.XmlUtil;
import org.dom4j.Document;
import org.dom4j.Element;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 贺瑞杰
 * @version V1.0
 * @date 2018-10-22 14:25
 * @description
 */
public class BeanFactoryImpl implements BeanFactory {

    /**
     * k：beanName v：实例化对象
     */
    private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();
    /**
     * k：beanName v：实例化对象的描述信息
     */
    private static final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    /**
     * 存放所有bean的名称
     */
    private static final Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());

    @Override
    public Object getBean(String beanName) {
        return null;
    }

    @Override
    public Object getBean(Class clazz) {
        return null;
    }

    private Object createBean(BeanDefinition beanDefinition) {
        Object obj = null;
        String className = beanDefinition.getClassPath();
        Class clazz = ClassUtil.loadClass(className);
        if (clazz == null) {
            return null;
        }
        List<ConstructorArg> constructorArgs = beanDefinition.getConstructorArgs();
        if (constructorArgs != null) {
            List<Class> classList = new ArrayList<>();
            List<Object> objectList = new ArrayList<>();
            for (int i = 0; i < constructorArgs.size(); i++) {
                ConstructorArg constructorArg = constructorArgs.get(i);
                if (constructorArg.getIndex() != null && !"".equals(constructorArg.getIndex())) {
                    int index = Integer.parseInt(constructorArg.getIndex());
                    addConstructorArg(objectList, classList, constructorArg, index);
                } else {
                    addConstructorArg(objectList, classList, constructorArg, i);
                }
            }
            Class[] classArray = new Class[classList.size()];
            Object[] objectArray = new Object[objectList.size()];
            classList.toArray(classArray);
            objectList.toArray(objectArray);
            obj = BeanUtil.instanceByCglib(clazz, classArray, objectArray);
        } else {
            obj = BeanUtil.instanceByCglib(clazz);
        }
        List<PropertyArg> propertyArgs = beanDefinition.getPropertyArgs();
        if (propertyArgs != null) {
            for (PropertyArg propertyArg : propertyArgs) {
                Object val = null;
                if (propertyArg.getRef() != null && !"".equals(propertyArg.getRef())) {
                    BeanDefinition bd = beanDefinitionMap.get(propertyArg.getRef());
                    if (bd != null) {
                        val = createBean(bd);
                    } else {
                        throw new RuntimeException("没有 " + propertyArg.getRef() + " 名称的bean");
                    }

                } else {
                    val = propertyArg.getValue();
                }
                try {
                    Field field = clazz.getDeclaredField(propertyArg.getName());
                    ClassUtil.executeSetMethod(obj, field, val);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }

    private void addConstructorArg(List<Object> objects, List<Class> classList, ConstructorArg constructorArg, int index) {
        if (constructorArg.getValue() != null && !"".equals(constructorArg.getValue())) {
            objects.add(index, ClassUtil.typeToValue(constructorArg.getType(), constructorArg.getValue()));
            classList.add(index, ClassUtil.strTypeToClass(constructorArg.getType()));
        }
        if (constructorArg.getRef() != null && !"".equals(constructorArg.getRef())) {
            Object ref = beanMap.get(constructorArg.getRef());
            if (ref != null) {
                objects.add(index, ref);
            } else {
                BeanDefinition bd = beanDefinitionMap.get(constructorArg.getRef());
                if (bd != null) {
                    objects.add(index, createBean(bd));
                    classList.add(index, ClassUtil.strTypeToClass(bd.getClassPath()));
                } else {
                    throw new RuntimeException("没有 " + constructorArg.getRef() + " 名称的bean");
                }
            }
        }
    }

    /**
     * 初始化BeanMap
     */
    public void initBeanMap() {
        for (String key : beanDefinitionMap.keySet()) {
            beanMap.put(key, createBean(beanDefinitionMap.get(key)));
        }
    }

    /**
     * 读取配置文件
     *
     * @param path 文件地址
     * @return 封装的Map<String                                                                                                                               ,                                                                                                                                                                                                                                                               BeanDefinition>
     * @throws RuntimeException
     */
    public void readConfigurationFiles(String path) throws RuntimeException {
        Document document = XmlUtil.getXmlByResourcesPath(path);
        List<Element> childElements = XmlUtil.getChildElement(document.getRootElement());
        for (Element node : childElements) {
            Map<String, String> map = XmlUtil.getAttributes(node, "id", "class");
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanName(map.get("id"));
            beanDefinition.setClassPath(map.get("class"));

            List<ConstructorArg> constructorArgs = null;
            List<PropertyArg> propertyArgs = null;

            List<Element> child = XmlUtil.getChildElement(node);
            for (Element c : child) {
                if ("constructor-arg".equals(c.getName())) {
                    if (constructorArgs == null) {
                        constructorArgs = new ArrayList<>();
                    }
                    Map<String, String> childMap = XmlUtil.getAttributes(c, "index", "name", "ref", "type", "value");
                    ConstructorArg constructorArg = new ConstructorArg();
                    constructorArg.setIndex(childMap.get("index"));
                    constructorArg.setName(childMap.get("name"));
                    constructorArg.setRef(childMap.get("ref"));
                    constructorArg.setType(childMap.get("type"));
                    constructorArg.setValue(childMap.get("value"));
                    constructorArgs.add(constructorArg);
                }
                if ("property".equals(c.getName())) {
                    if (propertyArgs == null) {
                        propertyArgs = new ArrayList<>();
                    }
                    Map<String, String> childMap = XmlUtil.getAttributes(c, "name", "ref", "value");
                    PropertyArg propertyArg = new PropertyArg();
                    propertyArg.setName(childMap.get("name"));
                    propertyArg.setRef(childMap.get("ref"));
                    propertyArg.setValue(childMap.get("value"));
                    propertyArgs.add(propertyArg);
                }
            }
            beanDefinition.setConstructorArgs(constructorArgs);
            beanDefinition.setPropertyArgs(propertyArgs);
            String beanName = map.get("id");
            if (beanDefinitionMap.get(beanName) == null) {
                beanDefinitionMap.put(beanName, beanDefinition);
            } else {
                throw new RuntimeException("不能有相同的id");
            }
        }
    }

    public static void main(String[] args) {
        /*Object o = new BeanFactoryImpl().createBean(new BeanDefinition("", "git.soulbgm.bean.BeanDefinition", null, null));
        String beanName = o.getClass().getSimpleName();
        beanName = beanName.substring(0, beanName.indexOf("$$"));
        System.out.println(beanName);
        System.out.println(o);*/
        BeanFactoryImpl beanFactory = new BeanFactoryImpl();
        beanFactory.readConfigurationFiles("beans.xml");
        beanFactory.initBeanMap();
        System.out.println(beanMap);
    }

}
